#include "CZ80.h"
#include <stdlib.h>
#include <stdio.h>

////////////////////////////////////////////////////////////////////////////////////////////////////

CZ80::CZ80() {
    mAF.w  = 0x01b0;
    mBC.w  = 0x0013;
    mDE.w  = 0x00d8;
    mHL.w  = 0x014d;
    mSP    = 0xfffe;
    mPC    = 0x0100;
    mRead  = NULL;
    mWrite = NULL;
    mTicks = 0;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

CZ80::~CZ80() {}

////////////////////////////////////////////////////////////////////////////////////////////////////

uint8_t CZ80::Fetch() {
    return mRead( mPC++ );
}

////////////////////////////////////////////////////////////////////////////////////////////////////

int CZ80::ExecuteOpcode( uint8_t opcode ) {
    mTicks = 0;

    printf( "\n%04X  %02X", mPC - 1, opcode );

    switch( opcode ) {
        // 8-bit alu
        case 0x04: mBC.h = INCREMENT_REG_8BIT( mBC.h ); break;
        case 0x05: mBC.h = DECREMENT_REG_8BIT( mBC.h ); break;
        case 0x0c: mBC.l = INCREMENT_REG_8BIT( mBC.l ); break;
        case 0x0d: mBC.l = DECREMENT_REG_8BIT( mBC.l ); break;
        case 0x14: mDE.h = INCREMENT_REG_8BIT( mDE.h ); break;
        case 0x15: mDE.h = DECREMENT_REG_8BIT( mDE.h ); break;
        case 0x1c: mDE.l = INCREMENT_REG_8BIT( mDE.l ); break;
        case 0x1d: mDE.l = DECREMENT_REG_8BIT( mDE.l ); break;
        case 0x24: mHL.h = INCREMENT_REG_8BIT( mHL.h ); break;
        case 0x25: mHL.h = DECREMENT_REG_8BIT( mHL.h ); break;
        case 0x2c: mHL.l = INCREMENT_REG_8BIT( mHL.l ); break;
        case 0x2d: mHL.l = DECREMENT_REG_8BIT( mHL.l ); break;
        case 0x34: mWrite( mHL.l, INCREMENT_REG_8BIT(mRead(mHL.l)) ); break;
        case 0x35: mWrite( mHL.w, DECREMENT_REG_8BIT(mRead(mHL.w)) ); break;
        case 0x3c: mAF.h = INCREMENT_REG_8BIT( mAF.h ); break;
        case 0x3d: mAF.h = DECREMENT_REG_8BIT( mAF.h ); break;
        case 0x80: ADD_8BIT( mBC.h ); break;
        case 0x81: ADD_8BIT( mBC.l ); break;
        case 0x82: ADD_8BIT( mDE.h ); break;
        case 0x83: ADD_8BIT( mDE.l ); break;
        case 0x84: ADD_8BIT( mHL.h ); break;
        case 0x85: ADD_8BIT( mHL.l ); break;
        case 0x86: ADD_8BIT( mRead(mHL.w) ); break;
        case 0x87: ADD_8BIT( mAF.h ); break;
        case 0xa0: AND_8BIT( mBC.h ); break;
        case 0xa1: AND_8BIT( mBC.l ); break;
        case 0xa2: AND_8BIT( mDE.h ); break;
        case 0xa3: AND_8BIT( mDE.l ); break;
        case 0xa4: AND_8BIT( mHL.h ); break;
        case 0xa5: AND_8BIT( mHL.l ); break;
        case 0xa6: AND_8BIT( mRead(mHL.w) ); break;
        case 0xa7: AND_8BIT( mAF.h ); break;
        case 0xa8: XOR_8BIT( mBC.h ); break;
        case 0xa9: XOR_8BIT( mBC.l ); break;
        case 0xaa: XOR_8BIT( mDE.h ); break;
        case 0xab: XOR_8BIT( mDE.l ); break;
        case 0xac: XOR_8BIT( mHL.h ); break;
        case 0xad: XOR_8BIT( mHL.l ); break;
        case 0xae: XOR_8BIT( mRead(mHL.w) ); break;
        case 0xaf: XOR_8BIT( mAF.h ); break;
        case 0xb0: OR_8BIT( mBC.h ); break;
        case 0xb1: OR_8BIT( mBC.l ); break;
        case 0xb2: OR_8BIT( mDE.h ); break;
        case 0xb3: OR_8BIT( mDE.l ); break;
        case 0xb4: OR_8BIT( mHL.h ); break;
        case 0xb5: OR_8BIT( mHL.l ); break;
        case 0xb6: OR_8BIT( mRead(mHL.w) ); break;
        case 0xb7: OR_8BIT( mAF.h ); break;
        case 0xb8: COMPARE_8BIT( mBC.h ); break;
        case 0xb9: COMPARE_8BIT( mBC.l ); break;
        case 0xba: COMPARE_8BIT( mDE.h ); break;
        case 0xbb: COMPARE_8BIT( mDE.l ); break;
        case 0xbc: COMPARE_8BIT( mHL.h ); break;
        case 0xbd: COMPARE_8BIT( mHL.l ); break;
        case 0xbe: COMPARE_8BIT( mRead(mHL.w) ); break;
        case 0xbf: COMPARE_8BIT( mAF.h ); break;
        case 0xc6: ADD_8BIT( Fetch() ); break;
        case 0xe6: AND_8BIT( Fetch() ); break;
        case 0xf6: OR_8BIT( Fetch() ); break;
        case 0xfe: COMPARE_8BIT( Fetch() ); break;

        // 8-bit loads
        case 0x02: mWrite( mBC.w, mAF.h ); break;
        case 0x06: LOAD_REG_8BIT( mBC.h, Fetch() ); break;
        case 0x0a: LOAD_REG_8BIT( mAF.h, mRead(mBC.w) ); break;
        case 0x0e: LOAD_REG_8BIT( mBC.l, Fetch() ); break;
        case 0x12: mWrite( mDE.w, mAF.h ); break;
        case 0x16: LOAD_REG_8BIT( mDE.h, Fetch() ); break;
        case 0x1a: LOAD_REG_8BIT( mAF.h, mRead(mDE.w) ); break;
        case 0x1e: LOAD_REG_8BIT( mDE.l, Fetch() ); break;
        case 0x22: mWrite( mHL.w++, mAF.h ); break;
        case 0x26: LOAD_REG_8BIT( mHL.h, Fetch() ); break;
        case 0x2a: LOAD_REG_8BIT( mAF.h, mRead(mHL.w++) ); break;
        case 0x2e: LOAD_REG_8BIT( mHL.l, Fetch() ); break;
        case 0x3e: LOAD_REG_8BIT( mAF.h, Fetch() ); break;
        case 0x47: LOAD_REG_8BIT( mBC.h, mAF.h ); break;
        case 0x4f: LOAD_REG_8BIT( mBC.l, mAF.h ); break;
        case 0x57: LOAD_REG_8BIT( mDE.h, mAF.h ); break;
        case 0x5f: LOAD_REG_8BIT( mDE.l, mAF.h ); break;
        case 0x67: LOAD_REG_8BIT( mHL.h, mAF.h ); break;
        case 0x68: LOAD_REG_8BIT( mHL.h, mBC.h ); break;
        case 0x6f: LOAD_REG_8BIT( mHL.l, mAF.h ); break;
        case 0x77: mWrite( mHL.w, mAF.h ); break;
        case 0x78: LOAD_REG_8BIT( mAF.h, mBC.h ); break;
        case 0x79: LOAD_REG_8BIT( mAF.h, mBC.l ); break;
        case 0x7a: LOAD_REG_8BIT( mAF.h, mDE.h ); break;
        case 0x7b: LOAD_REG_8BIT( mAF.h, mDE.l ); break;
        case 0x7c: LOAD_REG_8BIT( mAF.h, mHL.h ); break;
        case 0x7d: LOAD_REG_8BIT( mAF.h, mHL.l ); break;
        case 0x7e: LOAD_REG_8BIT( mAF.h, mRead(mHL.w) ); break;
        case 0x7f: LOAD_REG_8BIT( mAF.h, mAF.h ); break;
        case 0xe0: mWrite( (uint16_t)(0xff00 + Fetch()), mAF.h ); break;
        case 0xea: mWrite( (uint16_t)(Fetch() | (Fetch() << 8)), mAF.h ); break;
        case 0xf0: mAF.h = mRead( (uint16_t)(0xff00 + Fetch()) ); break;
        case 0xfa: LOAD_REG_8BIT( mAF.h, (uint16_t)(Fetch() | (Fetch() << 8)) ); break;

        // 16-bit loads
        case 0x01: LOAD_REG_16BIT( mBC.w ); break;
        case 0x08: LD_nn_SP(); break;
        case 0x11: LOAD_REG_16BIT( mDE.w ); break;
        case 0x21: LOAD_REG_16BIT( mHL.w ); break;
        case 0x31: LOAD_REG_16BIT( mSP ); break;
        case 0xc1: mBC.l = StackPop(); mBC.h = StackPop(); break;
        case 0xc5: StackPush( mBC.h ); StackPush( mBC.l ); break;
        case 0xd1: mDE.l = StackPop(); mDE.h = StackPop(); break;
        case 0xd5: StackPush( mDE.h ); StackPush( mDE.l ); break;
        case 0xe1: mHL.l = StackPop(); mHL.h = StackPop(); break;
        case 0xe5: StackPush( mHL.h ); StackPush( mHL.l ); break;
        case 0xf1: mAF.l = StackPop(); mAF.h = StackPop(); break;
        case 0xf5: StackPush( mAF.h ); StackPush( mAF.l ); break;
        case 0xf8: LDHL(); break;

        // 16-bit arithmetic
        case 0x03: mBC.w++; break;
        case 0x09: ADD_16BIT( mBC.w ); break;
        case 0x0b: mBC.w--; break;
        case 0x13: mDE.w++; break;
        case 0x19: ADD_16BIT( mDE.w ); break;
        case 0x1b: mDE.w--; break;
        case 0x2b: mHL.w--; break;
        case 0x23: mHL.w++; break;
        case 0x29: ADD_16BIT( mHL.w ); break;
        case 0x33: mSP++; break;
        case 0x39: ADD_16BIT( mSP ); break;
        case 0x3b: mSP--; break;

        // calls
        case 0xcd: CALL(); break;

        // jumps
        case 0x18: mPC += ( int8_t )Fetch(); break;
        case 0x20: JUMP_CONDITIONAL( !TestFlag(FLAG_Z) ); break;
        case 0x28: JUMP_CONDITIONAL( TestFlag(FLAG_Z) ); break;
        case 0x30: JUMP_CONDITIONAL( !TestFlag(FLAG_C) ); break;
        case 0x38: JUMP_CONDITIONAL( TestFlag(FLAG_C) ); break;
        case 0xc3: JUMP_IMMEDIATE(); break;

        // rotates & shifts
        case 0x0f: RRCA(); break;

        // returns
        case 0xc0: RETURN_CONDITIONAL( !TestFlag(FLAG_Z) ); break;
        case 0xc8: RETURN_CONDITIONAL( TestFlag(FLAG_Z) ); break;
        case 0xc9: mPC = ( uint16_t )( StackPop() | (StackPop() << 8) ); break;
        case 0xd0: RETURN_CONDITIONAL( !TestFlag(FLAG_C) ); break;
        case 0xd8: RETURN_CONDITIONAL( TestFlag(FLAG_C) ); break;

        // varios
        case 0x2f: // CPL -/-
            mAF.h = ( ~mAF.h );
            SetFlags( FLAG_N | FLAG_H );
            break;
        case 0xcb: // CB extended opcodes
            mTicks += ExecuteCBOpcode( Fetch() );
            break;
        case 0x00: // NOP -/-
        case 0xf3: // DI  -/- manejado desde afuera en CSTBoy::Run()
        case 0xfb: // EI  -/- manejado desde afuera en CSTBoy::Run()
            break;

        default: printf( "\nError: opcode invalido" ); exit( 0 );
    }

    printf( " AF:%04X BC:%04X DE:%04X HL:%04X SP:%04X", mAF.w, mBC.w, mDE.w, mHL.w, mSP );

    mTicks += OPCODE_CYCLES[ opcode ];

    return mTicks;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

int CZ80::ExecuteCBOpcode( uint8_t opcode ) {
    //printf( " %02X", opcode );

    switch( opcode ) {
        // bit opcodes
        case 0x40: TEST_BIT( mBC.h, 0 ); break;
        case 0x41: TEST_BIT( mBC.l, 0 ); break;
        case 0x42: TEST_BIT( mDE.h, 0 ); break;
        case 0x43: TEST_BIT( mDE.l, 0 ); break;
        case 0x44: TEST_BIT( mHL.h, 0 ); break;
        case 0x45: TEST_BIT( mHL.l, 0 ); break;
        case 0x46: TEST_BIT( mRead(mHL.w), 0 ); break;
        case 0x47: TEST_BIT( mAF.h, 0 ); break;
        case 0x48: TEST_BIT( mBC.h, 1 ); break;
        case 0x49: TEST_BIT( mBC.l, 1 ); break;
        case 0x4a: TEST_BIT( mDE.h, 1 ); break;
        case 0x4b: TEST_BIT( mDE.l, 1 ); break;
        case 0x4c: TEST_BIT( mHL.h, 1 ); break;
        case 0x4d: TEST_BIT( mHL.l, 1 ); break;
        case 0x4e: TEST_BIT( mRead(mHL.w), 1 ); break;
        case 0x4f: TEST_BIT( mAF.h, 1 ); break;
        case 0x50: TEST_BIT( mBC.h, 2 ); break;
        case 0x51: TEST_BIT( mBC.l, 2 ); break;
        case 0x52: TEST_BIT( mDE.h, 2 ); break;
        case 0x53: TEST_BIT( mDE.l, 2 ); break;
        case 0x54: TEST_BIT( mHL.h, 2 ); break;
        case 0x55: TEST_BIT( mHL.l, 2 ); break;
        case 0x56: TEST_BIT( mRead(mHL.w), 2 ); break;
        case 0x57: TEST_BIT( mAF.h, 2 ); break;
        case 0x58: TEST_BIT( mBC.h, 3 ); break;
        case 0x59: TEST_BIT( mBC.l, 3 ); break;
        case 0x5a: TEST_BIT( mDE.h, 3 ); break;
        case 0x5b: TEST_BIT( mDE.l, 3 ); break;
        case 0x5c: TEST_BIT( mHL.h, 3 ); break;
        case 0x5d: TEST_BIT( mHL.l, 3 ); break;
        case 0x5e: TEST_BIT( mRead(mHL.w), 3 ); break;
        case 0x5f: TEST_BIT( mAF.h, 3 ); break;
        case 0x60: TEST_BIT( mBC.h, 4 ); break;
        case 0x61: TEST_BIT( mBC.l, 4 ); break;
        case 0x62: TEST_BIT( mDE.h, 4 ); break;
        case 0x63: TEST_BIT( mDE.l, 4 ); break;
        case 0x64: TEST_BIT( mHL.h, 4 ); break;
        case 0x65: TEST_BIT( mHL.l, 4 ); break;
        case 0x66: TEST_BIT( mRead(mHL.w), 4 ); break;
        case 0x67: TEST_BIT( mAF.h, 4 ); break;
        case 0x68: TEST_BIT( mBC.h, 5 ); break;
        case 0x69: TEST_BIT( mBC.l, 5 ); break;
        case 0x6a: TEST_BIT( mDE.h, 5 ); break;
        case 0x6b: TEST_BIT( mDE.l, 5 ); break;
        case 0x6c: TEST_BIT( mHL.h, 5 ); break;
        case 0x6d: TEST_BIT( mHL.l, 5 ); break;
        case 0x6e: TEST_BIT( mRead(mHL.w), 5 ); break;
        case 0x6f: TEST_BIT( mAF.h, 5 ); break;
        case 0x70: TEST_BIT( mBC.h, 6 ); break;
        case 0x71: TEST_BIT( mBC.l, 6 ); break;
        case 0x72: TEST_BIT( mDE.h, 6 ); break;
        case 0x73: TEST_BIT( mDE.l, 6 ); break;
        case 0x74: TEST_BIT( mHL.h, 6 ); break;
        case 0x75: TEST_BIT( mHL.l, 6 ); break;
        case 0x76: TEST_BIT( mRead(mHL.w), 6 ); break;
        case 0x77: TEST_BIT( mAF.h, 6 ); break;
        case 0x78: TEST_BIT( mBC.h, 7 ); break;
        case 0x79: TEST_BIT( mBC.l, 7 ); break;
        case 0x7a: TEST_BIT( mDE.h, 7 ); break;
        case 0x7b: TEST_BIT( mDE.l, 7 ); break;
        case 0x7c: TEST_BIT( mHL.h, 7 ); break;
        case 0x7d: TEST_BIT( mHL.l, 7 ); break;
        case 0x7e: TEST_BIT( mRead(mHL.w), 7 ); break;
        case 0x7f: TEST_BIT( mAF.h, 7 ); break;
        case 0x80: mBC.h = RESET_BIT( mBC.h, 0 ); break;
        case 0x81: mBC.l = RESET_BIT( mBC.l, 0 ); break;
        case 0x82: mDE.h = RESET_BIT( mDE.h, 0 ); break;
        case 0x83: mDE.l = RESET_BIT( mDE.l, 0 ); break;
        case 0x84: mHL.h = RESET_BIT( mHL.h, 0 ); break;
        case 0x85: mHL.l = RESET_BIT( mHL.l, 0 ); break;
        case 0x86: mWrite( mHL.w, RESET_BIT(mRead(mHL.w), 0) ); break;
        case 0x87: mAF.h = RESET_BIT( mAF.h, 0 ); break;
        case 0x88: mBC.h = RESET_BIT( mBC.h, 1 ); break;
        case 0x89: mBC.l = RESET_BIT( mBC.l, 1 ); break;
        case 0x8a: mDE.h = RESET_BIT( mDE.h, 1 ); break;
        case 0x8b: mDE.l = RESET_BIT( mDE.l, 1 ); break;
        case 0x8c: mHL.h = RESET_BIT( mHL.h, 1 ); break;
        case 0x8d: mHL.l = RESET_BIT( mHL.l, 1 ); break;
        case 0x8e: mWrite( mHL.w, RESET_BIT(mRead(mHL.w), 1) ); break;
        case 0x8f: mAF.h = RESET_BIT( mAF.h, 1 ); break;
        case 0x90: mBC.h = RESET_BIT( mBC.h, 2 ); break;
        case 0x91: mBC.l = RESET_BIT( mBC.l, 2 ); break;
        case 0x92: mDE.h = RESET_BIT( mDE.h, 2 ); break;
        case 0x93: mDE.l = RESET_BIT( mDE.l, 2 ); break;
        case 0x94: mHL.h = RESET_BIT( mHL.h, 2 ); break;
        case 0x95: mHL.l = RESET_BIT( mHL.l, 2 ); break;
        case 0x96: mWrite( mHL.w, RESET_BIT(mRead(mHL.w), 2) ); break;
        case 0x97: mAF.h = RESET_BIT( mAF.h, 2 ); break;
        case 0x98: mBC.h = RESET_BIT( mBC.h, 3 ); break;
        case 0x99: mBC.l = RESET_BIT( mBC.l, 3 ); break;
        case 0x9a: mDE.h = RESET_BIT( mDE.h, 3 ); break;
        case 0x9b: mDE.l = RESET_BIT( mDE.l, 3 ); break;
        case 0x9c: mHL.h = RESET_BIT( mHL.h, 3 ); break;
        case 0x9d: mHL.l = RESET_BIT( mHL.l, 3 ); break;
        case 0x9e: mWrite( mHL.w, RESET_BIT(mRead(mHL.w), 3) ); break;
        case 0x9f: mAF.h = RESET_BIT( mAF.h, 3 ); break;
        case 0xa0: mBC.h = RESET_BIT( mBC.h, 4 ); break;
        case 0xa1: mBC.l = RESET_BIT( mBC.l, 4 ); break;
        case 0xa2: mDE.h = RESET_BIT( mDE.h, 4 ); break;
        case 0xa3: mDE.l = RESET_BIT( mDE.l, 4 ); break;
        case 0xa4: mHL.h = RESET_BIT( mHL.h, 4 ); break;
        case 0xa5: mHL.l = RESET_BIT( mHL.l, 4 ); break;
        case 0xa6: mWrite( mHL.w, RESET_BIT(mRead(mHL.w), 4) ); break;
        case 0xa7: mAF.h = RESET_BIT( mAF.h, 4 ); break;
        case 0xa8: mBC.h = RESET_BIT( mBC.h, 5 ); break;
        case 0xa9: mBC.l = RESET_BIT( mBC.l, 5 ); break;
        case 0xaa: mDE.h = RESET_BIT( mDE.h, 5 ); break;
        case 0xab: mDE.l = RESET_BIT( mDE.l, 5 ); break;
        case 0xac: mHL.h = RESET_BIT( mHL.h, 5 ); break;
        case 0xad: mHL.l = RESET_BIT( mHL.l, 5 ); break;
        case 0xae: mWrite( mHL.w, RESET_BIT(mRead(mHL.w), 5) ); break;
        case 0xaf: mAF.h = RESET_BIT( mAF.h, 5 ); break;
        case 0xb0: mBC.h = RESET_BIT( mBC.h, 6 ); break;
        case 0xb1: mBC.l = RESET_BIT( mBC.l, 6 ); break;
        case 0xb2: mDE.h = RESET_BIT( mDE.h, 6 ); break;
        case 0xb3: mDE.l = RESET_BIT( mDE.l, 6 ); break;
        case 0xb4: mHL.h = RESET_BIT( mHL.h, 6 ); break;
        case 0xb5: mHL.l = RESET_BIT( mHL.l, 6 ); break;
        case 0xb6: mWrite( mHL.w, RESET_BIT(mRead(mHL.w), 6) ); break;
        case 0xb7: mAF.h = RESET_BIT( mAF.h, 6 ); break;
        case 0xb8: mBC.h = RESET_BIT( mBC.h, 7 ); break;
        case 0xb9: mBC.l = RESET_BIT( mBC.l, 7 ); break;
        case 0xba: mDE.h = RESET_BIT( mDE.h, 7 ); break;
        case 0xbb: mDE.l = RESET_BIT( mDE.l, 7 ); break;
        case 0xbc: mHL.h = RESET_BIT( mHL.h, 7 ); break;
        case 0xbd: mHL.l = RESET_BIT( mHL.l, 7 ); break;
        case 0xbe: mWrite( mHL.w, RESET_BIT(mRead(mHL.w), 7) ); break;
        case 0xbf: mAF.h = RESET_BIT( mAF.h, 7 ); break;

        // varios
        case 0x30: mBC.h = SWAP_NIBBLES( mBC.h ); break;
        case 0x31: mBC.l = SWAP_NIBBLES( mBC.l ); break;
        case 0x32: mDE.h = SWAP_NIBBLES( mDE.h ); break;
        case 0x33: mDE.l = SWAP_NIBBLES( mDE.l ); break;
        case 0x34: mHL.h = SWAP_NIBBLES( mHL.h ); break;
        case 0x35: mHL.l = SWAP_NIBBLES( mHL.l ); break;
        case 0x36: mWrite( mHL.w, SWAP_NIBBLES(mRead(mHL.w)) ); break;
        case 0x37: mAF.h = SWAP_NIBBLES( mAF.h ); break;

        default: printf( "\nError: opcode invalido" ); exit( 0 );
    }

    return CB_OPCODE_CYCLES[ opcode ];
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void CZ80::StackPush( uint8_t value ) {
    mWrite( --mSP, value );
}

////////////////////////////////////////////////////////////////////////////////////////////////////

uint8_t CZ80::StackPop() {
    return mRead( mSP++ );
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void CZ80::ClearFlags( uint8_t flags ) {
    mAF.l &= ~flags;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void CZ80::SetFlags( uint8_t flags ) {
    mAF.l |= flags;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

bool CZ80::TestFlag( uint8_t flag ) {
    return ( (uint8_t)(mAF.l & flag) == flag );
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void CZ80::ADD_8BIT( uint8_t value ) {
    uint8_t result = mAF.h + value;
    ClearFlags( FLAG_Z | FLAG_N | FLAG_H | FLAG_C );
    if( result == 0 ) SetFlags( FLAG_Z );
    if( (mAF.h & 0x0f) + (value & 0x0f) > 0x0f ) SetFlags( FLAG_H );
    if( (uint16_t)(mAF.h + value) > 0xff ) SetFlags( FLAG_C );
    mAF.h = result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void CZ80::ADD_16BIT( uint16_t value ) {
    uint16_t result = mHL.w + value;
    ClearFlags( FLAG_N | FLAG_H | FLAG_C );
    if( (mHL.w & 0x0fff) + (value & 0x0fff) > 0x0fff ) SetFlags( FLAG_H );
    if( (uint32_t)(mHL.w + value) > 0xffff ) SetFlags( FLAG_C );
    mHL.w = result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void CZ80::AND_8BIT( uint8_t value ) {
    mAF.h &= value;
    ClearFlags( FLAG_Z | FLAG_N | FLAG_C );
    if( mAF.h == 0 ) SetFlags( FLAG_Z );
    SetFlags( FLAG_H );
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void CZ80::CALL() {
    uint16_t address = ( uint16_t )( Fetch() | (Fetch() << 8) );
    StackPush( (uint8_t)(mPC >> 8) );
    StackPush( (uint8_t)(mPC & 0x00ff) );
    mPC = address;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void CZ80::COMPARE_8BIT( uint8_t value ) {
    uint8_t result = mAF.h - value;
    ClearFlags( FLAG_Z | FLAG_H | FLAG_C );
    if( mAF.h == value ) SetFlags( FLAG_Z );
    SetFlags( FLAG_N );
    if( (result & 0x0f) > (mAF.h & 0x0f) ) SetFlags( FLAG_H );
    if( mAF.h < value ) SetFlags( FLAG_C );
}

////////////////////////////////////////////////////////////////////////////////////////////////////

uint8_t CZ80::DECREMENT_REG_8BIT( uint8_t reg ) {
    uint8_t before = reg--;
    ClearFlags( FLAG_Z | FLAG_H );
    if( reg == 0 ) SetFlags( FLAG_Z );
    SetFlags( FLAG_N );
    if( (uint8_t)(before & 0x0f) == 0 ) SetFlags( FLAG_H );

    return reg;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void CZ80::LD_nn_SP() {
    uint16_t address = ( uint16_t )( Fetch() | (Fetch() << 8) );
    mWrite( address++, (uint8_t)(mSP & 0xff) );
    mWrite( address, (uint8_t)(mSP >> 8) );
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void CZ80::LDHL() {
    int8_t value = ( int8_t )Fetch();
    ClearFlags( FLAG_Z | FLAG_N | FLAG_H | FLAG_C );
    if( (uint32_t)(mSP + value) > 0xffff ) SetFlags( FLAG_C );
    if( (mSP & 0x0f) + (value & 0x0f) > 0x0f ) SetFlags( FLAG_H );
    mHL.w = ( uint16_t )( mSP + value );
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void CZ80::LOAD_REG_8BIT( uint8_t& dest, uint8_t src ) {
    dest = src;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void CZ80::LOAD_REG_16BIT( uint16_t& reg ) {
    reg = ( uint16_t )( Fetch() | (Fetch() << 8) );
}

////////////////////////////////////////////////////////////////////////////////////////////////////

uint8_t CZ80::INCREMENT_REG_8BIT( uint8_t reg ) {
    uint8_t before = reg++;
    ClearFlags( FLAG_Z | FLAG_N | FLAG_H );
    if( reg == 0 ) SetFlags( FLAG_Z );
    if( (uint8_t)(before & 0x0f) == 0x0f ) SetFlags( FLAG_H );

    return reg;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void CZ80::JUMP_CONDITIONAL( bool condition ) {
    mPC += ( condition ? (int8_t)Fetch() : 1 );
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void CZ80::JUMP_IMMEDIATE() {
    mPC = ( uint16_t )( Fetch() | (Fetch() << 8) );
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void CZ80::OR_8BIT( uint8_t value ) {
    mAF.h |= value;
    ClearFlags( FLAG_Z | FLAG_N | FLAG_H | FLAG_C );
    if( mAF.h == 0 ) SetFlags( FLAG_Z );
}

////////////////////////////////////////////////////////////////////////////////////////////////////

uint8_t CZ80::RESET_BIT( uint8_t reg, int bit ) {
    reg &= ~( 1 << bit );

    return reg;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void CZ80::RETURN_CONDITIONAL( bool condition ) {
    if( condition ) mPC = ( uint16_t )( StackPop() | (StackPop() << 8) );
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void CZ80::RRCA() {
    uint8_t carry = ( mAF.h & 0x01 );
    mAF.h >>= 1;
    mAF.h |= ( carry << 7 );
    ClearFlags( FLAG_Z | FLAG_N | FLAG_H | FLAG_C );
    if( mAF.h == 0 ) SetFlags( FLAG_Z );
    if( carry ) SetFlags( FLAG_C );
}

////////////////////////////////////////////////////////////////////////////////////////////////////

uint8_t CZ80::SWAP_NIBBLES( uint8_t reg ) {
    uint8_t l = ( uint8_t )( reg & 0x0f );
    uint8_t h = ( uint8_t )( reg & 0xf0 );
    reg = ( uint8_t )( (l << 4) | (h >> 4) );
    ClearFlags( FLAG_Z | FLAG_N | FLAG_H | FLAG_C );
    if( reg == 0 ) SetFlags( FLAG_Z );

    return reg;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void CZ80::TEST_BIT( uint8_t reg, int bit ) {
    uint8_t mask = ( uint8_t )( 1 << bit );
    ClearFlags( FLAG_Z | FLAG_N );
    if( (uint8_t)(reg & mask) == 0 ) SetFlags( FLAG_Z );
    SetFlags( FLAG_H );
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void CZ80::XOR_8BIT( uint8_t value ) {
    mAF.h ^= value;
    ClearFlags( FLAG_Z | FLAG_N | FLAG_H | FLAG_C );
    if( mAF.h == 0 ) SetFlags( FLAG_Z );
}

////////////////////////////////////////////////////////////////////////////////////////////////////